home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Toolbox / Fragment Tool / Sources / Fragments.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-20  |  14.2 KB  |  594 lines  |  [TEXT/MPCC]

  1. /*
  2.     File:        Fragments.c
  3.  
  4.     Contains:    Code Fragment manipulation routines
  5.  
  6.     Written by:    Chris White, Developer Technical Support
  7.     
  8.     Copyright:    © 1995 by Apple Computer, Inc., all rights reserved.
  9.     
  10.     Change History (most recent first):
  11.     
  12.                   9/28/95    CW        First release
  13.  
  14. */
  15.  
  16. #ifndef __MEMORY__
  17.     #include <Memory.h>
  18. #endif
  19.  
  20. #ifndef __RESOURCES__
  21.     #include <Resources.h>
  22. #endif
  23.  
  24. #ifndef __STDDEF__
  25.     #include <stddef.h>
  26. #endif
  27.  
  28. #ifndef __STRING__
  29.     #include <string.h>
  30. #endif
  31.  
  32.  
  33.  
  34. #ifndef __FRAGMENTTOOL__
  35.     #include "FragmentTool.h"
  36. #endif
  37.  
  38. #ifndef __FRAGMENTSTUFF__
  39.     #include "FragmentStuff.h"
  40. #endif
  41.  
  42. #include "Prototypes.h"
  43.  
  44.  
  45. #include "Utilities.h"
  46.  
  47.  
  48.  
  49. static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount );
  50. static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength );
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57. //
  58. // Translates the 'cfrg' resource into our own data structure. Ours will
  59. // contain the same information in a simpler format, plus a few extra fields.
  60. //
  61. OSErr ParseResource ( Handle theResource, tHeaderHan privateData )
  62. {
  63.     // Copy the relevant items from the 'cfrg' resource into our internal format
  64.     SignedByte    oldResourceState, oldInternalState;
  65.     short        itemCount, index;
  66.     Ptr            itemStart;
  67.     long        headerSize = offsetof(cfrgHeader, arrayStart);
  68.     OSErr        err = noErr;
  69.     
  70.     
  71.     // set the size of the destination block
  72.     itemCount = (*(hdrHand)theResource)->itemCount;
  73.     err = SetInternalResourceSize ( (Handle) privateData, itemCount );
  74.     if ( err )
  75.         goto done;
  76.     
  77.     // Lock the original resource
  78.     oldResourceState = HGetState ( theResource );
  79.     HLock ( theResource );
  80.     
  81.     oldInternalState = HGetState ( (Handle) privateData );
  82.     HLock ( (Handle) privateData );
  83.     
  84.     // Copy the relevant fields from the header
  85.     (*privateData)->version = (*(hdrHand)theResource)->version;
  86.     (*privateData)->itemCount = itemCount;
  87.     
  88.     // Transfer each item from the cfrg into the internal resource
  89.     if (itemCount == 0) goto done;
  90.     itemStart = &(*(hdrHand)theResource)->arrayStart;
  91.     for (index = 0; index < itemCount; index++) {
  92.         cfrgItem*         srcItem;
  93.         tItemPtr         dstItem;
  94.         
  95.         srcItem = (cfrgItem*)itemStart;
  96.         dstItem = &(*privateData)->itemList[index];
  97.         
  98.         // These are just used internally
  99.         dstItem->bDeleted = false;
  100.         dstItem->bExistsInDocument = true;
  101.         dstItem->tempFilePtr = nil;
  102.         
  103.         // These contain the actual fargement data
  104.         dstItem->archType        = srcItem->archType;
  105.         dstItem->updateLevel    = srcItem->updateLevel;
  106.         dstItem->currVersion    = srcItem->currVersion;
  107.         dstItem->oldDefVersion    = srcItem->oldDefVersion;
  108.         dstItem->appStackSize    = srcItem->appStackSize;
  109.         dstItem->appSubFolder    = srcItem->appSubFolder;
  110.         dstItem->usage            = srcItem->usage;
  111.         dstItem->location        = srcItem->location;
  112.         dstItem->codeOffset        = srcItem->codeOffset;
  113.         dstItem->codeLength        = srcItem->codeLength;
  114.         BlockMove(srcItem->name, dstItem->name, srcItem->name[0]+1);
  115.         
  116.         itemStart += srcItem->itemSize;
  117.     }
  118.  
  119. done:
  120.     
  121.     HSetState ( theResource, oldResourceState );
  122.     HSetState ( (Handle) privateData, oldInternalState );
  123.     
  124.     return err;    
  125. }
  126.  
  127.  
  128.  
  129. //
  130. // Build the 'cfrg' resource from our own data structures.
  131. //
  132. OSErr BuildResource ( tHeaderHan privateData, Handle theResource )
  133. {
  134.     // Construct a cfrg resource from our internal template
  135.     SignedByte        oldResourceState, oldInternalState;
  136.     OSErr            err;
  137.     unsigned long    headerSize =  offsetof(cfrgHeader, arrayStart);
  138.     unsigned long    bytesCopied;
  139.     int                itemCount, deletedCount = 0, index;
  140.  
  141.     // Construct the header by setting the destination handle to that
  142.     // size, clearing the memory, and then inserting the version and
  143.     // item count values
  144.     oldResourceState = HGetState(theResource);
  145.     oldInternalState = HGetState((Handle)privateData);
  146.     HLock((Handle)privateData);
  147.     
  148.     SetHandleSize(theResource, headerSize);
  149.     err = MemError ( );
  150.     if ( err ) goto done;
  151.     
  152.     BlockClear ( *theResource, 0, headerSize );
  153.     
  154.     ((cfrgHeader*)(*theResource))->version = (*privateData)->version;
  155.     itemCount = (*privateData)->itemCount;
  156.     ((cfrgHeader*)(*theResource))->itemCount = itemCount;
  157.     
  158.     // Now, copy each item individually
  159.     bytesCopied = headerSize;
  160.     for (index = 0; index < itemCount; index++)
  161.     {
  162.         cfrgItem*        dstPtr;
  163.         tItemPtr        srcPtr;
  164.         long            itemSize;
  165.         unsigned long    newSize;
  166.         
  167.         srcPtr = &(*privateData)->itemList[index];
  168.         if ( srcPtr->bDeleted )
  169.         {
  170.             deletedCount++;
  171.             continue;
  172.         }
  173.         
  174.         // Calculate the size of this entry
  175.         itemSize = offsetof(cfrgItem, name) + srcPtr->name[0] + 1;
  176.         itemSize += itemSize & 0x0003;    // Pad up to the next multiple of 4
  177.         
  178.         // Extend and clear the handle
  179.         newSize = bytesCopied + itemSize;
  180.         SetHandleSize(theResource, newSize);
  181.         err = MemError ( );
  182.         if ( err ) goto done;
  183.         dstPtr = (cfrgItem*)(((unsigned long)*theResource) + bytesCopied);
  184.         BlockClear ( (Ptr)dstPtr, 0, itemSize );
  185.         
  186.         // Transfer the individual fields
  187.         dstPtr->archType        = srcPtr->archType;
  188.         dstPtr->updateLevel        = srcPtr->updateLevel;
  189.         dstPtr->currVersion        = srcPtr->currVersion;
  190.         dstPtr->oldDefVersion    = srcPtr->oldDefVersion;
  191.         dstPtr->appStackSize    = srcPtr->appStackSize;
  192.         dstPtr->appSubFolder    = srcPtr->appSubFolder;
  193.         dstPtr->usage            = srcPtr->usage;
  194.         dstPtr->location        = srcPtr->location;
  195.         dstPtr->codeOffset        = srcPtr->codeOffset;
  196.         dstPtr->codeLength        = srcPtr->codeLength;
  197.         dstPtr->itemSize        = itemSize;
  198.         BlockMove(srcPtr->name, dstPtr->name, srcPtr->name[0] + 1);
  199.  
  200.         bytesCopied = newSize;
  201.     }
  202.     
  203.     ((cfrgHeader*)(*theResource))->itemCount -= deletedCount;
  204.     
  205.     
  206. done:
  207.     HSetState(theResource, oldResourceState);
  208.     HSetState((Handle)privateData, oldInternalState);
  209.     return err;
  210. }
  211.  
  212.  
  213.  
  214. OSErr CopyFragment ( tHeaderHan sourceHeader, FSSpecPtr sourceSpec, int16 sourceIndex,
  215.                         tHeaderHan targetHeader, FSSpecPtr targetSpec )
  216. {
  217.     SignedByte        sourceState;
  218.     SignedByte        targetState;
  219.     int16            targetIndex = 0;
  220.     OSErr            theErr;
  221.     tItemPtr        targetPtr = nil;
  222.     
  223.     
  224.     // No need to use HLockHi. It's slower, and they're not locked for long
  225.     // enough for it to make any difference here.
  226.     sourceState = HGetState ( (Handle) sourceHeader ); HLock ( (Handle) sourceHeader );
  227.     targetState = HGetState ( (Handle) targetHeader ); HLock ( (Handle) targetHeader );
  228.     
  229.     // Resize the handle based on the new item count
  230.     theErr = SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount + 1 );
  231.     if ( theErr ) goto CleanupAndBail;
  232.     
  233.     // We're appending the data, so the target index is itemCount (Index in zero-based)
  234.     targetIndex = (*targetHeader)->itemCount;
  235.     
  236.     targetPtr = &(*targetHeader)->itemList[targetIndex];
  237.     BlockMoveData ( &(*sourceHeader)->itemList[sourceIndex], targetPtr, sizeof ( tItem ) );
  238.     if ( sourceSpec && targetSpec )
  239.     {
  240.         theErr = AppendFileData ( sourceSpec, targetSpec, &targetPtr->codeOffset, &targetPtr->codeLength );
  241.         if ( theErr ) goto CleanupAndBail;
  242.         
  243.     }
  244.     
  245.     // Success. Now it's safe to update the itemCount
  246.     (*targetHeader)->itemCount++;
  247.     
  248.     return noErr;
  249.     
  250.     
  251. CleanupAndBail:
  252.     
  253.     // Unlock our handles
  254.     HSetState ( (Handle) sourceHeader, sourceState );
  255.     HSetState ( (Handle) targetHeader, targetState );
  256.     // Release any extra storage we may have grabbed
  257.     SetInternalResourceSize ( (Handle) targetHeader, (*targetHeader)->itemCount );
  258.     
  259.     return theErr;
  260. }
  261.  
  262.  
  263.  
  264. //
  265. // This is called twice to actualy delete a fragment. The first time to update
  266. // the data structures we keep in memory. The second time is when the user saves
  267. // the document, and we actually delete the fragment from the data fork.
  268. //
  269. OSErr DeleteFragment ( tHeaderHan theHeader, FSSpecPtr theSpec, int16 theIndex )
  270. {
  271.     SignedByte    theState;
  272.     OSErr        theErr = noErr;
  273.     tItemPtr     theItem = nil;
  274.     
  275.     
  276.     theState = HGetState ( (Handle) theHeader );
  277.     HLock ( (Handle) theHeader );
  278.     
  279.     theItem = GetNthItem ( theHeader, theIndex );
  280.     if ( theItem == nil )
  281.         return kGenericError;
  282.     
  283.     // If the item isn't already marked as deleted, do so
  284.     // and update the header with the new item count.
  285.     if ( !theItem->bDeleted )
  286.         theItem->bDeleted = true;
  287.     
  288.     // If we have a file spec, we need to actually delete the data now.
  289.     if ( theSpec )
  290.     {
  291.         int i;
  292.         
  293.         theErr = DeleteFileData ( theSpec, theItem->codeOffset, theItem->codeLength );
  294.         
  295.         // We need to update the offsets off all the fragments that follow this one
  296.         for ( i = theIndex + 1; i < (*theHeader)->itemCount; i++ )
  297.         {
  298.             tItemPtr    tmpItem;
  299.             
  300.             tmpItem = GetNthItem ( theHeader, i );
  301.             tmpItem->codeOffset -= theItem->codeLength;
  302.         }
  303.     }
  304.     
  305.     HSetState ( (Handle) theHeader, theState );
  306.     
  307.     return noErr;
  308. }
  309.  
  310.  
  311.  
  312. //
  313. // Gets the record pointer given an index
  314. //
  315. tItemPtr GetNthItem ( tHeaderHan theHeader, int16 theIndex )
  316. {
  317.     tItemPtr theItem = nil;
  318.     
  319.     if ( (*theHeader)->itemCount > theIndex )
  320.         theItem = &(*theHeader)->itemList[theIndex];
  321.         
  322.     #if DEBUGGING
  323.     if ( theItem == nil )    DebugStr ( "\p GetNthItem returning nil" );
  324.     #endif
  325.     
  326.     return theItem;
  327. }
  328.  
  329.  
  330.  
  331. //
  332. // Gets the last item. Used for when an item has just been added.
  333. //
  334. tItemPtr GetLastItem ( tHeaderHan theHeader )
  335. {
  336.     int16        theIndex;
  337.     tItemPtr     theItem = nil;
  338.     
  339.     theIndex = (*theHeader)->itemCount - 1;
  340.     theItem = &(*theHeader)->itemList[theIndex];
  341.     
  342.     #if DEBUGGING
  343.     if ( theItem == nil )    DebugStr ( "\p GetLastItem returning nil" );
  344.     #endif
  345.     
  346.     return theItem;
  347. }
  348.  
  349.  
  350.  
  351. //
  352. // Returns the number of fragments currently in the document
  353. //
  354. int16 GetItemCount ( tHeaderHan theHeader )
  355. {
  356.     return (*theHeader)->itemCount;
  357. }
  358.  
  359.  
  360.  
  361. //
  362. // Sets the handle size based on the number of items we pass in
  363. //
  364. static OSErr SetInternalResourceSize ( Handle theHan, int16 itemCount )
  365. {
  366.     SignedByte    theState;
  367.     OSErr        theErr;
  368.     
  369.     // We need to pass SetHandleSize an unlocked handle, but we'll
  370.     // preserve its present state.
  371.     theState = HGetState ( theHan );
  372.     HUnlock ( theHan );
  373.     
  374.     // set the size of the resource based in the item count
  375.     SetHandleSize ( theHan, offsetof ( tHeader, itemList ) +
  376.                                 (itemCount * sizeof ( tItem )) );
  377.     theErr = MemError ( );
  378.     HSetState ( theHan, theState );
  379.     
  380.     return theErr;
  381. }
  382.  
  383.  
  384.  
  385. //
  386. // Appends the data in source at given offset of length to target file.
  387. // The offset of the new data in target is returned in offset.
  388. //
  389. OSErr AppendFileData ( FSSpecPtr source, FSSpecPtr target, long* offset, long* length )
  390. {
  391.     OSErr    theErr;
  392.     short    refNum;
  393.     Ptr        theData;
  394.     long    inOutCount;
  395.     
  396.     
  397.     
  398.     //
  399.     // TO DO:
  400.     //        Don't want to depend on having a large enough block
  401.     //        for the entire fragment. Try our heap, then temporary
  402.     //        memory, and finally resort to doing it bit by bit.
  403.     //
  404.     
  405.     theErr = FSpOpenDF ( source, fsRdPerm, &refNum );
  406.     
  407.     // A length of zero indicates ‘until the EOF’. We'll update
  408.     // the length now, and return it back to the caller.
  409.     if ( *length == 0 )
  410.         theErr = GetEOF ( refNum, length );
  411.     
  412.     theData = NewPtr ( *length );
  413.     if ( theData )
  414.     {
  415.         inOutCount = *length;
  416.         // On entry, ‘offset’ is the offset to the data in source file
  417.         theErr = SetFPos ( refNum, fsFromStart, *offset );
  418.         theErr = FSRead ( refNum, &inOutCount, theData );
  419.         FSClose ( refNum );
  420.         
  421.         FSpOpenDF ( target, fsRdWrPerm, &refNum );
  422.         theErr = SetFPos ( refNum, fsFromLEOF, 0L );
  423.         // On exit, ‘offset’ is the offset to the data in target file
  424.         theErr = GetFPos ( refNum, offset );
  425.         inOutCount = *length;
  426.         theErr = FSWrite ( refNum, &inOutCount, theData );    
  427.         
  428.         // Dispose of the buffer
  429.         DisposePtr ( theData );
  430.     }
  431.     
  432.     // Close the file - ‘source’ if memory alloc failed, ‘target’ if sucessful
  433.     FSClose ( refNum );
  434.     
  435.     
  436.     return noErr;
  437. }
  438.  
  439.  
  440.  
  441. //
  442. //    Deletes the block of data from the data fork at a given offset and length.
  443. //    It achieves this in one of two ways. If the data to be deleted is at the
  444. //    logical EOF, it simply resets the logical EOF to the given offset. If data
  445. //    exists after the data to be deleted, that data is read and writen back at
  446. //    the given offset. 
  447. // 
  448. static OSErr DeleteFileData ( FSSpecPtr theSpec, int32 theOffset, int32 theLength )
  449. {
  450.     OSErr    theErr;
  451.     int16    theRef;
  452.     int32    theEOF;
  453.     int32    inOutCount;
  454.     Ptr        theData;
  455.     
  456.     
  457.     
  458.     theErr = FSpOpenDF ( theSpec, fsRdWrPerm, &theRef );
  459.     if ( theErr )
  460.         return theErr;
  461.     
  462.     theErr = GetEOF ( theRef, &theEOF );
  463.     
  464.     // A length of zero indicates ‘until the EOF’
  465.     if ( theLength == 0 )
  466.         theLength = theEOF - theOffset;
  467.     
  468.     
  469.     // Is the block to be removed at the EOF? For readablility, I'll keep
  470.     // this ‘if’ seperate from the above (length == 0) special condition.
  471.     if ( theEOF == theOffset + theLength )
  472.         theErr = SetEOF ( theRef, theOffset );
  473.     else
  474.     {
  475.         // Data exists after the block to be deleted
  476.         theData = NewPtr ( theEOF - (theOffset + theLength) );
  477.         if ( theData )
  478.         {
  479.             theErr = SetFPos ( theRef, fsFromStart, theOffset + theLength );
  480.             inOutCount = theEOF - (theOffset + theLength);
  481.             theErr = FSRead ( theRef, &inOutCount, theData );
  482.             
  483.             theErr = SetFPos ( theRef, fsFromStart, theOffset );
  484.             inOutCount = theEOF - (theOffset + theLength);
  485.             theErr = FSWrite ( theRef, &inOutCount, theData );
  486.             
  487.             theErr = SetEOF ( theRef, theOffset + inOutCount );
  488.         }
  489.     }
  490.     
  491.     FSClose ( theRef );
  492.     
  493.     return noErr;
  494. }
  495.  
  496.  
  497.  
  498. //
  499. // Each fragment's data stored in a temp file has a single temp
  500. // record. It includes a usage count and the fileSpec. If an item
  501. // doesn't have a record, this routine creates it as it increments
  502. // its usage count.
  503. //
  504. OSErr IncrementTempUsageCount ( tItemPtr theItem )
  505. {
  506.     OSErr    theErr;
  507.     
  508.     if ( theItem->tempFilePtr == nil )
  509.     {
  510.         theItem->tempFilePtr = (tTempFilePtr) NewPtrClear ( sizeof ( tTempFileRec ) );
  511.         theErr = MemError ( );
  512.         if ( theErr )
  513.             return theErr;
  514.         
  515.     }
  516.     
  517.     theItem->tempFilePtr->usageCount++;
  518.     
  519.     return noErr;
  520. }
  521.  
  522.  
  523.  
  524. //
  525. // As each document gets its own copy of the fragment, it decrements the
  526. // usage count. Finally, the temp file is deleted and this storage freed.
  527. //
  528. OSErr DecrementTempUsageCount ( tItemPtr theItem )
  529. {    
  530.     OSErr    theErr;
  531.     
  532.     
  533.     if ( theItem->tempFilePtr )
  534.     {
  535.         theItem->tempFilePtr->usageCount--;
  536.         if ( theItem->tempFilePtr->usageCount == 0 )
  537.         {
  538.             theErr = FSpDelete ( &theItem->tempFilePtr->fileSpec );
  539.             if ( theErr )
  540.                 return theErr;
  541.                 
  542.             DisposePtr ( (Ptr) theItem->tempFilePtr );
  543.             theErr = MemError ( );
  544.             if ( theErr )
  545.                 return theErr;
  546.                 
  547.         }
  548.         theItem->tempFilePtr = nil;
  549.     }
  550.     
  551.     
  552.     return noErr;
  553. }
  554.  
  555.  
  556.  
  557. //
  558. // Find out how many documents are using this temp record
  559. //
  560. int GetTempUsageCount ( tItemPtr theItem )
  561. {
  562.     int theCount = 0;
  563.     
  564.     if ( theItem->tempFilePtr )
  565.         theCount = theItem->tempFilePtr->usageCount;
  566.         
  567.     return theCount;
  568. }
  569.  
  570.  
  571.  
  572. //
  573. // Get the fileSpec for the fragments data
  574. //
  575. FSSpecPtr GetTempSpecPtr ( tItemPtr theItem )
  576. {
  577.     FSSpecPtr    theSpec = nil;
  578.     
  579.     if ( theItem->tempFilePtr )
  580.         theSpec = &theItem->tempFilePtr->fileSpec;
  581.     
  582.     
  583.     #if DEBUGGING
  584.     if ( theSpec == nil )    DebugStr ( "\p GetTempSpecPtr returning nil" );
  585.     #endif
  586.     
  587.     return theSpec;
  588. }
  589.  
  590.  
  591.  
  592.  
  593.  
  594.